#ifndef _X_BYTE_BUFFER_H
#define _X_BYTE_BUFFER_H
#include "xDef.h"
#include "xLog.h"
#include <vector>
#include <sstream>
#include <string.h>
namespace xNet
{
    NET_ENGINE_CORE_API class xByteBufferException : public std::exception
    {
    public:
        xByteBufferException(std::string exceptStr = ""):_msg(exceptStr){}
        ~xByteBufferException() noexcept = default;
        char const* what() const noexcept override { return _msg.c_str(); }
    protected:
        std::string & message() noexcept { return _msg; }
    private:
        std::string _msg;
    };

    NET_ENGINE_CORE_API class xByteBufferPosException : public xByteBufferException
    {
        public:
            xByteBufferPosException(bool add, size_t pos, size_t size, size_t valueSize)
            {
                std::ostringstream ss;
                ss << "Attempted to " << (add ? "put" : "get") << " value with size: "
                << valueSize << " in ByteBuffer (pos: " << pos << " size: " << size
                << ")";

                message().assign(ss.str());
            }
            ~xByteBufferPosException() noexcept = default;
    };

    NET_ENGINE_CORE_API class xByteBuffer
    {
        public:
            xByteBuffer(uint32 defaultSize = 1024,uint8 power = 2)
                      : _maxSize(defaultSize)
                      , _power(power)
                      , _rPos(0)
                      , _wPos(0)
            {
                _storage.clear();
                _storage.resize(_maxSize,0);
            }
            ~xByteBuffer()
            {
                _storage.clear();
                std::vector<uint8>().swap(_storage);
                _maxSize = _rPos = _wPos = 0;
            }
        public:
            uint32 ReadSize()
            {
                return _wPos - _rPos;
            }
            uint32 WriteSize()
            {
                return _maxSize - _wPos;
            }
            uint8* ReadBegin()
            {
                return &_storage[_rPos];
            }
            uint8* WriteBegin()
            {
                return &_storage[_wPos];
            }
            void ReSize(uint32 resize)
            {
                _storage.resize(resize,0);
                _maxSize = resize;
                _rPos = _wPos = 0;
            }
            uint32 Size() const
            {
                return _storage.size();
            }
            void AddRPos(uint32 pos)
            {
                _rPos += pos;
            }
            void AddWPos(uint32 pos)
            {
                _wPos += pos;
            } 
        private:
            template<typename T>
            T Read()
            {
                if(ReadSize() < sizeof(T))
                {
                    return T();
                }
                T t;
                memcpy(&t,&_storage[_rPos],sizeof(T));
                _rPos += sizeof(T);
                ZeroPosIfEqual();
                return t;
            }
            void Read(uint8* data,uint32 len)
            {
                if(len > ReadSize() || _rPos > _wPos)
                {
                    throw xByteBufferPosException(false,_rPos,len,Size());
                }
                memcpy(data,ReadBegin(),len);
                _rPos += len;
                ZeroPosIfEqual();
            }
            template<typename T>
            void Read(T& data)
            {
                Read((uint8*)&data,sizeof(T));
            }
            void Write(const uint8* data,uint32 len)
            {
                if(_rPos > _wPos)
                {
                    throw xByteBufferPosException(true,_wPos,len,Size());
                }
                Expand(len);
                memcpy(&_storage[_wPos],data,len);
                _wPos += len;
            }
            template<typename T>
            void Write(const T& data)
            {
                Write((const uint8*)&data,sizeof(T));
            }
        public:
            xByteBuffer& operator<<(const uint8& data)
            {
                Write<uint8>(data);
                return *this;
            }
            xByteBuffer& operator<<(const int8& data)
            {
                Write<int8>(data);
                return *this;
            }
            xByteBuffer& operator<<(const uint16& data)
            {
                Write<uint16>(data);
                return *this;
            }
            xByteBuffer& operator<<(const int16& data)
            {
                Write<int16>(data);
                return *this;
            }
            xByteBuffer& operator<<(const uint32& data)
            {
                Write<uint32>(data);
                return *this;
            }
            xByteBuffer& operator<<(const int32& data)
            {
                Write<int32>(data);
                return *this;
            }
            xByteBuffer& operator<<(const uint64& data)
            {
                Write<uint64>(data);
                return *this;
            }
            xByteBuffer& operator<<(const int64& data)
            {
                Write<int64>(data);
                return *this;
            }
            xByteBuffer& operator<<(const float& data)
            {
                Write<float>(data);
                return *this;
            }
            xByteBuffer& operator<<(const double& data)
            {
                Write<double>(data);
                return *this;
            }
            xByteBuffer& operator<<(const char* str)
            {
                Write((const uint8*)str,str ? strlen(str) : 0);
                Write<uint8>(0);
                return *this;
            }
            xByteBuffer& operator<<(const std::string& data)
            {
                (*this) << data.c_str();
                return *this;
            }
        public:
            xByteBuffer& operator>>(uint8& data)
            {
                Read<uint8>(data);
                return *this;
            }
            xByteBuffer& operator>>(int8& data)
            {
                Read<int8>(data);
                return *this;
            }
            xByteBuffer& operator>>(uint16& data)
            {
                Read<uint16>(data);
                return *this;
            }
            xByteBuffer& operator>>(int16& data)
            {
                Read<int16>(data);
                return *this;
            }
            xByteBuffer& operator>>(uint32& data)
            {
                Read<uint32>(data);
                return *this;
            }
            xByteBuffer& operator>>(int32& data)
            {
                Read<int32>(data);
                return *this;
            }    
            xByteBuffer& operator>>(uint64& data)
            {
                Read<uint64>(data);
                return *this;
            }
            xByteBuffer& operator>>(int64& data)
            {
                Read<int64>(data);
                return *this;
            } 
            xByteBuffer& operator>>(double& data)
            {
                Read<double>(data);
                return *this;
            }
            xByteBuffer& operator>>(float& data)
            {
                Read<float>(data);
                return *this;
            }    
            xByteBuffer& operator>>(char* data)
            {
                std::string tmp;
                (*this) >> tmp;
                memcpy(data,tmp.c_str(),tmp.size());
                return *this;
            }
            xByteBuffer& operator>>(std::string& data)
            {
                data.clear();
                while(_rPos < _wPos)
                {
                    char c = Read<char>();
                    if(c == 0)
                    {
                        break;
                    }
                    data += c;
                }
                return *this;
            }
        public:
            xByteBuffer& operator>>(xByteBuffer& buffer)
            {
                uint32 len = 0;
                Read<uint32>(len);
                if(len > 0)
                {
                    buffer.ReSize(len);
                    Read(buffer.WriteBegin(),len);
                }
                return *this;
            }
            xByteBuffer& operator<<(xByteBuffer& buffer)
            {
                uint32 len = buffer.ReadSize();
                Write(len);
                if(len > 0)
                {
                    Write(buffer.ReadBegin(),len);
                }
                return *this;
            }
        private:
            void ZeroPosIfEqual()
            {
                if(_rPos == _wPos)
                {
                    _rPos = _wPos = 0;
                }
            }
        public:
            void Expand(uint32 cnt)
            {
                if(cnt <= WriteSize())
                {
                    return ;
                }
                uint32 newSize = _maxSize * _power;
                while(newSize - ReadSize() <= cnt)
                {
                    newSize *= _power;
                }
                _maxSize = newSize;
                _storage.resize(_maxSize,0);

                int enReadSize = ReadSize();
                if(enReadSize > 0 && _rPos > 0)
                {
                    memcpy(&_storage[0],&_storage[_rPos],enReadSize);
                    _wPos = enReadSize;
                    _rPos = 0;
                }
            }

        protected:
            std::vector<uint8> _storage;
            uint32             _rPos;
            uint32             _wPos;
            uint8              _power;
            uint32             _maxSize;             
    };
}

#endif